package ar.uade.edu.fain.ad.server.service;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import ar.uade.edu.fain.ad.common.exception.MesaNoDisponibleException;
import ar.uade.edu.fain.ad.common.exception.PedidoCerradoException;
import ar.uade.edu.fain.ad.common.exception.ProductoNoDisponibleException;
import ar.uade.edu.fain.ad.common.remote.SalonServicioRemoto;
import ar.uade.edu.fain.ad.common.transfer.ComandaVO;
import ar.uade.edu.fain.ad.common.transfer.MesaVO;
import ar.uade.edu.fain.ad.common.transfer.MozoVO;
import ar.uade.edu.fain.ad.common.transfer.PedidoVO;
import ar.uade.edu.fain.ad.common.transfer.SalonVO;
import ar.uade.edu.fain.ad.common.transfer.SectorVO;
import ar.uade.edu.fain.ad.server.business.Mesa;
import ar.uade.edu.fain.ad.server.business.MesaSimple;
import ar.uade.edu.fain.ad.server.business.Mozo;
import ar.uade.edu.fain.ad.server.business.Pedido;
import ar.uade.edu.fain.ad.server.business.Plato;
import ar.uade.edu.fain.ad.server.business.Sector;
import ar.uade.edu.fain.ad.server.dao.MesaDAO;
import ar.uade.edu.fain.ad.server.dao.MozoDAO;
import ar.uade.edu.fain.ad.server.dao.PedidoDAO;
import ar.uade.edu.fain.ad.server.dao.PlatoDAO;
import ar.uade.edu.fain.ad.server.dao.SectorDAO;
import ar.uade.edu.fain.ad.server.util.persistence.TransactionManager;

public class SalonSRV extends UnicastRemoteObject implements
		SalonServicioRemoto {

	private static final long serialVersionUID = 1L;

	private TransactionManager transactionManager;
	
	private SectorDAO sectorDAO;

	private MesaDAO mesaDAO;

	private MozoDAO mozoDAO; 

	private PedidoDAO pedidoDAO;
	
	private PlatoDAO platoDAO;
	
	public SalonSRV() throws RemoteException {
		super();
	}

	public TransactionManager getTransactionManager() {
		return transactionManager;
	}

	public void setTransactionManager(TransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public PlatoDAO getPlatoDAO() {
		return platoDAO;
	}

	public void setPlatoDAO(PlatoDAO platoDAO) {
		this.platoDAO = platoDAO;
	}

	public PedidoDAO getPedidoDAO() {
		return pedidoDAO;
	}

	public void setPedidoDAO(PedidoDAO pedidoDAO) {
		this.pedidoDAO = pedidoDAO;
	}

	public MozoDAO getMozoDAO() {
		return mozoDAO;
	}

	public void setMozoDAO(MozoDAO mozoDAO) {
		this.mozoDAO = mozoDAO;
	}

	public SectorDAO getSectorDAO() {
		return sectorDAO;
	}

	public void setSectorDAO(SectorDAO sectorDAO) {
		this.sectorDAO = sectorDAO;
	}

	public MesaDAO getMesaDAO() {
		return mesaDAO;
	}

	public void setMesaDAO(MesaDAO mesaDAO) {
		this.mesaDAO = mesaDAO;
	}

	@Override
	public List<SectorVO> buscarSectoresEn(final SalonVO salonVO)
			throws RemoteException {
		ArrayList<SectorVO> vos = null;
		try {
			getTransactionManager().start();
			final List<Sector> sectores = 
					getSectorDAO().buscarSectoresDe(salonVO.getId());
			vos = new ArrayList<SectorVO>();
			for (Sector sector : sectores) {
				vos.add(sector.toVO());
			}
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return vos;
	}
	
	@Override
	public Set<MesaVO> buscarMesasDisponiblesEn(
			final SectorVO sectorVO,
			final Integer cantidadComensales) throws RemoteException {
		HashSet<MesaVO> vos;
		try {
			getTransactionManager().start();
			if (cantidadComensales == null) {
				throw new IllegalArgumentException(
						"Se necesita una cantidad de comensales.");
			}
			Sector sector = getSectorDAO().get(sectorVO.getId());
			final List<Mesa> disponibles = 
					sector.ubicarMesasPara(cantidadComensales);
			vos = new HashSet<MesaVO>();
			for (Mesa mesa : disponibles) {
				final MesaVO vo = mesa.toVO();
				vos.add(vo);
			}
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return vos;
	}

	@Override
	public synchronized PedidoVO abrirPedido(
			final MozoVO mozoVO, final Set<MesaVO> mesaVOs)
			throws RemoteException, MesaNoDisponibleException {
		final Set<MesaSimple> mesas = new HashSet<MesaSimple>();
		PedidoVO vo;
		try {
			getTransactionManager().start();
			for (MesaVO mesaVO : mesaVOs) {
				final MesaSimple mesa = (MesaSimple) getMesaDAO().get(mesaVO.getId());
				mesas.add(mesa);
			}
			final Sector sector = mesas.iterator().next().getSector();
			final Mesa mesaPreparada = sector.prepararMesa(mesas);
			final Pedido pedido = mesaPreparada.abrir();
			final Mozo mozo = getMozoDAO().get(mozoVO.getId());
			pedido.asociarA(mozo);
			if (mesaPreparada.getId() == null) {
				getMesaDAO().save(mesaPreparada);
			} else {
				getMesaDAO().update(mesaPreparada);
			}
			getPedidoDAO().save(pedido);
			vo = pedido.toVO();
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return vo;
	}

	@Override
	public void agregarComanda(
			final PedidoVO pedidoVO, final ComandaVO comandaVO)
			throws RemoteException, ProductoNoDisponibleException {
		try {
			getTransactionManager().start();
			final Pedido pedido = getPedidoDAO().get(pedidoVO.getId());
			if (pedido == null) {
				throw new IllegalArgumentException(
						"ID de Pedido " + pedidoVO.getId() + " invalido.");
			}
			final Plato plato = getPlatoDAO().get(comandaVO.getPlato().getId());
			pedido.agregar(plato, comandaVO.getCantidad());
			getPedidoDAO().update(pedido);
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
	}

	@Override
	public Set<MesaVO> buscarMesasOcupadas(final SalonVO salonVO) 
			throws RemoteException {
		HashSet<MesaVO> results;
		try {
			getTransactionManager().start();
			final List<Mesa> ocupadas = getMesaDAO().buscarMesas(
					salonVO.getId(), Mesa.MESA_OCUPADA);
			results = new HashSet<MesaVO>();
			for (Mesa mesa : ocupadas) {
				results.add(mesa.toVO());
			}
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return results;
	}

	@Override
	public Set<PedidoVO> buscarPedidosActivos(final SalonVO salonVO)
			throws RemoteException {
		HashSet<PedidoVO> vos;
		try {
			getTransactionManager().start();
			final List<Pedido> pedidos = 
					getPedidoDAO().buscarPedidosActivos(salonVO.getId());
			vos = new HashSet<PedidoVO>();
			for (Pedido pedido : pedidos) {
				vos.add(pedido.toVO());
			}
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		}
		return vos;
	}
	
	@Override
	public void cerrarPedido(final PedidoVO pedidoVO)
			throws PedidoCerradoException, RemoteException {
		try {
			getTransactionManager().start();
			final Pedido pedido = getPedidoDAO().get(pedidoVO.getId());
			final Mesa mesa = pedido.getMesa();
			pedido.cerrar();
			mesa.liberar();
			getMesaDAO().update(mesa);
			getPedidoDAO().update(pedido);
			getTransactionManager().commit();
		} catch (RuntimeException e) {
			getTransactionManager().rollback();
			throw e;
		} catch (PedidoCerradoException e) {
			getTransactionManager().rollback();
			throw e;
		}
	}

}
